import { ref, Ref, nextTick } from "vue"
import { PageData } from "types/pageData"
import { PageQuery } from "types/pageQuery"
import { ElNotification, ElMessageBox, FormInstance, TableInstance } from "element-plus"
import { DialogData, DictionaryOptions, TableData, TableTreeData, TreeSelectData } from "./types/default"
import type Node from "element-plus/es/components/tree/src/model/node"
import { Dictionary } from "@/api/system-api/dictionary/types/dictionary"
import { getListByParentId } from "@/api/system-api/dictionary/index"
import { TreeInstance } from "element-plus/lib/components/index.js"
/**
 * 通用封装 根据promise获取请求返回结果
 * @param query 查询实体
 * @param queryFunc 查询promise
 * @returns PageData(非响应式)
 */
export async function getPageListMember<TQuery extends PageQuery, TResult>(
  query: Ref<TQuery>,
  queryFunc: (query: PageQuery) => Promise<ApiResponseData<PageData<TResult>>>,
  loading: Ref<boolean>,
  showNotice: boolean = true
): Promise<PageData<TResult>> {
  let items = {} as PageData<TResult>
  loading.value = true
  await queryFunc(query.value)
    .then((res) => {
      if (res.code === 2000) {
        if (showNotice) {
          showNotification()
        }
        items = res.items
      }
    })
    .finally(() => (loading.value = false))
  return items
}
/**
 * 获取el-table所需要的所有参数
 * @param query 查询实体(不需要响应式)
 * @param queryApi 获取数据的api方法
 * @param showNotice 获取数据成功是否显示notice
 * @returns 详见接口 TableData
 */
export function useTableDataMember<TQuery extends PageQuery, TResult>(
  query: TQuery,
  queryApi: (query: PageQuery) => Promise<ApiResponseData<PageData<TResult>>>,
  showNotice: boolean = true
): TableData<TQuery, TResult> {
  const temp = Object.assign({}, query)
  const tableRef = ref<TableInstance>() as Ref<TableInstance>
  const tableDataRef = ref<TResult[]>()
  const totalRef = ref(0)
  const tableLoadingRef = ref(true)
  const queryRef = ref(query) as Ref<TQuery>
  function resetQuery(): void {
    queryRef.value = temp
  }
  async function getList(): Promise<void> {
    const res = await getPageListMember(queryRef, queryApi, tableLoadingRef, showNotice)
    tableDataRef.value = res.data
    totalRef.value = res.totalCount || 0
  }
  async function handleSizeChange(val: number): Promise<void> {
    queryRef.value.limit = val
    await getList()
  }
  async function handleCurrentChange(val: number): Promise<void> {
    queryRef.value.page = val
    await getList()
  }
  return {
    tableRef,
    tableLoadingRef,
    queryRef,
    tableDataRef,
    totalRef,
    resetQuery,
    getList,
    handleSizeChange,
    handleCurrentChange
  }
}
/**
 * 获取懒加载树形el-table的所有成员
 * @param query 查询实体
 * @param queryApi 查询列表的api方法(此api必须支持获取子节点,并且接收query作为查询参数)
 * @param resultKey 第一个key为当前节点的主键用来查询子节点数据，第二个key为当前节点记录父级节点的key用来处理增加子节点刷新当前父节点下所有数据，第三个key作为查询子节点查询参数的名称。
 * @param queryKey 父级查询的value作为query的key
 * @param showNotice 获取数据成功是否显示notice
 * @returns 详见接口 TableTreeData
 */
export function useTableTreeDataMember<TQuery extends PageQuery, TResult>(
  query: TQuery,
  queryApi: (query: PageQuery) => Promise<ApiResponseData<PageData<TResult>>>,
  childKeyMap: [primaryKey: keyof TResult, parentKey: keyof TResult, queryKey: keyof TQuery],
  showNotice: boolean = true
): TableTreeData<TQuery, TResult> {
  const tableMemeber = useTableDataMember(query, queryApi, showNotice)
  const map = new Map<any, { row: TResult; treeNode: any; resolve: any }>()
  async function load(row: TResult, treeNode: any, resolve: (data: TResult[]) => void): Promise<void> {
    const key = row[childKeyMap[0]] as any
    map.set(key, { row, treeNode, resolve })
    const temp = { page: 1, limit: 100 } as TQuery
    temp[childKeyMap[2]] = key
    const temRef = ref(temp)
    const child = await getPageListMember(temRef, queryApi, tableMemeber.tableLoadingRef, false)
    if (child.totalCount > 0) {
      resolve(child.data)
    } else {
      resolve([])
    }
  }
  async function reload(row?: TResult): Promise<void> {
    const key = row![childKeyMap[1]]
    if (map.has(key)) {
      const { row, treeNode, resolve } = map.get(key) || { row: {} as TResult }
      tableMemeber.tableRef.value.store.states.lazyTreeNodeMap.value[key] = []
      await load(row, treeNode, resolve)
    } else {
      tableMemeber.getList()
    }
  }
  return {
    tableRef: tableMemeber.tableRef,
    queryRef: tableMemeber.queryRef,
    tableLoadingRef: tableMemeber.tableLoadingRef,
    tableDataRef: tableMemeber.tableDataRef,
    totalRef: tableMemeber.totalRef,
    resetQuery: tableMemeber.resetQuery,
    getList: tableMemeber.getList,
    handleSizeChange: tableMemeber.handleSizeChange,
    handleCurrentChange: tableMemeber.handleSizeChange,
    load: load,
    reload: reload
  }
}
/**
 * 获取数据表单dialog所有用到的对象
 * @param entity 表单模型(非响应式)
 * @param addApi 新增数据的api
 * @param updateApi 修改数据的api
 * @param deleteApi 删除的api
 * @param deleteKeyMaps 构造删除请求时实体和删除请求的映射关系数组 第一个key是实体中作为参数传递的数据 第二个key是作为删除操作的参数
 * @param getListFunc 更新table数据的方法
 * @returns
 */
export function useDataDialogMember<TEntity, TAddResult, TUpdateResult, TDeleteResult>(
  entity: TEntity,
  addApi: (entity: TEntity) => Promise<ApiResponseData<TAddResult>>,
  updateApi: (entity: TEntity) => Promise<ApiResponseData<TUpdateResult>>,
  //addOrUnpdatePred: (entity: TEntity) => boolean,
  deleteApi: (del: any) => Promise<ApiResponseData<TDeleteResult>>,
  deleteKeyMaps: [keyof TEntity, string][],
  getListFunc: (row?: TEntity) => Promise<void>
): DialogData<TEntity> {
  const entityCopy = JSON.stringify(entity)
  const formRef = ref<FormInstance>() as Ref<FormInstance>
  const entityRef = ref<TEntity>(entity) as Ref<TEntity>
  const dialogStatus = ref<boolean>(false)
  const currentRef = ref(0) as Ref<0 | 1 | 2 | 3>
  const map = new Map<0 | 1 | 2 | 3, string>([
    [0, "初始值"],
    [1, "新增"],
    [2, "修改"],
    [3, "详情"]
  ])
  function getTemp(): TEntity {
    const temp = Object.assign({}, entityRef.value) as TEntity
    return temp
  }
  function resetFields(func?: () => void): void {
    nextTick(() => {
      formRef.value?.resetFields()
      func?.()
    })
  }
  async function handleCreate(): Promise<void> {
    currentRef.value = 1
    entityRef.value = JSON.parse(entityCopy)
    dialogStatus.value = true
    resetFields()
  }
  async function handleUpdate(entity: TEntity): Promise<void> {
    const temp = Object.assign({}, entity)
    currentRef.value = 2
    dialogStatus.value = true
    resetFields(() => {
      entityRef.value = temp
    })
  }
  async function handleDelete(entity: TEntity): Promise<void> {
    const temp = {} as any
    deleteKeyMaps.forEach((t) => {
      temp[t[1]] = entity[t[0]] as any
    })
    ElMessageBox.confirm("您确定删除此数据吗?数据不可恢复", "警告", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
      center: true
    }).then(async () => {
      await deleteApi(temp).then(async (res) => {
        if (res.code === 2001) {
          showNotification("删除成功")
          await getListFunc(entity)
        } else {
          showNotification("删除失败", "error")
        }
      })
    })
  }
  function handleDetail(entity: TEntity): void {
    currentRef.value = 3
    dialogStatus.value = true
    resetFields(() => {
      entityRef.value = entity
    })
  }
  async function submit(): Promise<void> {
    const temp = getTemp()
    formRef.value?.validate(async (valid) => {
      if (valid) {
        //addOrUnpdatePred(entityRef.value)
        if (currentRef.value === 1) {
          await handleSubmit(temp, addApi, getListFunc, "保存成功", dialogStatus)
        } else if (currentRef.value === 2) {
          await handleSubmit(temp, updateApi, getListFunc, "修改成功", dialogStatus)
        }
      }
    })
  }
  function getCurrent(): string {
    return map.get(currentRef.value)!
  }
  // watch(dialogStatus,(nval:boolean,oval:boolean)=>{
  //   if(!nval){
  //     currentStatus=0
  //   }
  // })
  return {
    formRef,
    entityRef,
    dialogStatus,
    currentRef,
    getCurrent,
    handleCreate,
    handleUpdate,
    handleDelete,
    handleDetail,
    submit
  }
}
async function handleSubmit<T, TResult>(
  entity: T,
  submitFunc: (data: T) => Promise<ApiResponseData<TResult>>,
  getListFunc: (data?: T) => Promise<void>,
  message: string,
  status: Ref<boolean>
): Promise<void> {
  await submitFunc(entity).then(async (res) => {
    if (res.code === 2001) {
      showNotification(message)
      await getListFunc(entity)
      status.value = false
    } else {
      showNotification(message, "error")
    }
  })
}
/**
 * 获取树形组件所有用到的对象
 * @param treeApi 获取数据的api
 * @param query 查询参数
 * @param childKeyMap 第一个key作为Node中获取下级数据的实参，第二个key作为查询子节点的查询参数
 * @returns
 */
export function useTreeMember<TQuery, TResult>(
  treeApi: (query?: TQuery) => Promise<ApiResponseData<TResult[]>>,
  query?: TQuery,
  childKeyMap?: [keyof Node, keyof TQuery]
): TreeSelectData<TResult> {
  const optionsRef = ref<TResult[]>() as Ref<TResult[]>
  const treeRef = ref<TreeInstance>() as Ref<TreeInstance>
  async function getTree(): Promise<void> {
    await treeApi(query).then((res) => {
      if (res.code === 2000) {
        const items = res.items
        optionsRef.value = items
      }
    })
  }
  async function load(node: Node, resolve: (data: TResult[]) => void, reject: () => void): Promise<void> {
    if (childKeyMap != null && childKeyMap != undefined) {
      const temp = {} as TQuery
      temp[childKeyMap[1]] = node[childKeyMap[0]] as any
      await treeApi(temp).then((res) => {
        if (res.code === 2000) {
          resolve(res.items)
        } else {
          reject()
        }
      })
    }
  }
  return { optionsRef, getTree, treeLoad: load, treeRef }
}
/**
 * 根据父级id快速获取el-select的选项
 * @param parentId 字典父id
 * @returns
 */
export function useDictionaryOptions(parentId: number): DictionaryOptions {
  const optionsRef = ref<Dictionary[]>() as Ref<Dictionary[]>
  const temp = {
    parentId: parentId
  }
  async function getOptions(): Promise<void> {
    await getListByParentId(temp).then((res) => {
      if (res.code === 2000) {
        optionsRef.value = res.items
      }
    })
  }
  return { optionsRef, getOptions }
}

function showNotification(message: string = "查询成功", type: "success" | "error" | "info" | "warning" = "success") {
  ElNotification({
    title: "成功",
    message: message,
    type: type,
    position: "bottom-right"
  })
}
